//
//  ThreadDAO.m
//
//  Created by djr on 16/2/17.
//

#import "ThreadDAO.h"
#import <libkern/OSAtomic.h>
#import <UIKit/UIKit.h>

static dispatch_queue_t GetLimitAsyncQueue() {
#define MAX_QUEUE_COUNT 5
    static int queueCount;
    static dispatch_queue_t queues[MAX_QUEUE_COUNT];
    static dispatch_once_t onceToken;
    static int32_t counter = 0;
    dispatch_once(&onceToken, ^{
        queueCount = (int)[NSProcessInfo processInfo].activeProcessorCount;
        queueCount = queueCount < 1 ? 1 : queueCount > MAX_QUEUE_COUNT ? MAX_QUEUE_COUNT : queueCount;
        if ([UIDevice currentDevice].systemVersion.floatValue >= 8.0) {
            for (NSUInteger i = 0; i < queueCount; i++) {
                dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INITIATED, 0);
                queues[i] = dispatch_queue_create("com.ThreadDAO.LimitAsyncQueue", attr);
            }
        } else {
            for (NSUInteger i = 0; i < queueCount; i++) {
                queues[i] = dispatch_queue_create("com.ThreadDAO.LimitAsyncQueue", DISPATCH_QUEUE_SERIAL);
                dispatch_set_target_queue(queues[i], dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
            }
        }
    });
    int32_t cur = OSAtomicIncrement32(&counter);
    if (cur < 0) cur = - cur;
    return queues[(cur) % queueCount];
#undef MAX_QUEUE_COUNT
}

@interface ThreadDAO ()

@property (strong, nonatomic) dispatch_queue_t globalQueue;
@property (strong, nonatomic) dispatch_queue_t concurrentQueue;
@property (strong, nonatomic) dispatch_queue_t serialQueue;
@property (strong, nonatomic) dispatch_queue_t mainQueue;

#pragma mark - 串行队列 异步执行等待
/** 群组队列 */
@property (nonatomic, strong) NSMapTable *groupQueues;
/** 队列 */
@property (nonatomic, strong) NSMapTable *queues;
/** 计算次数 */
@property (nonatomic, strong) NSMutableDictionary *targetIdCounts;

@end

@implementation ThreadDAO

+ (void)initialize {
    if (self == [ThreadDAO self])  {
        ThreadDAO *thread = [ThreadDAO sharedInstance];
        thread.mainQueue    = dispatch_get_main_queue();
        thread.globalQueue  = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        thread.concurrentQueue  = dispatch_queue_create("com.ThreadDAO.ConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);
        thread.serialQueue  = dispatch_queue_create("com.ThreadDAO.SerialQueue", DISPATCH_QUEUE_SERIAL);
        thread.groupQueues = [NSMapTable mapTableWithKeyOptions:NSPointerFunctionsWeakMemory valueOptions:NSPointerFunctionsStrongMemory];
        thread.queues = [NSMapTable mapTableWithKeyOptions:NSPointerFunctionsWeakMemory valueOptions:NSPointerFunctionsStrongMemory];
        thread.targetIdCounts = [NSMutableDictionary dictionary];
    }
}

+ (instancetype)sharedInstance
{
    static ThreadDAO *dao = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        dao = [[ThreadDAO alloc] init];
    });
    return dao;
}

#pragma mark - /** 创建子线程 */
+ (void)executeInGlobalQueue:(dispatch_block_t)block
{
    NSParameterAssert(block);
    
    dispatch_async([ThreadDAO sharedInstance].globalQueue, block);
}
+ (void)executeInGlobalQueue:(dispatch_block_t)block afterDelaySecs:(NSTimeInterval)sec
{
    
    NSParameterAssert(block);
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * sec), [ThreadDAO sharedInstance].globalQueue, block);
}

#pragma mark /** 创建GCD自定义子线程 */
+ (void)executeInCustomGlobalQueue:(dispatch_queue_t)globalQueue block:(dispatch_block_t)block
{
    NSParameterAssert(block);
    dispatch_async(globalQueue, block);
}

#pragma mark /** 创建GCD同步自定义子线程 */
+ (void)executeInSyncCustomGlobalQueue:(dispatch_queue_t)globalQueue block:(dispatch_block_t)block
{
    NSParameterAssert(block);
    dispatch_sync(globalQueue, block);
}

+ (void)executeInCustomGlobalQueue:(dispatch_queue_t)globalQueue block:(dispatch_block_t)block afterDelaySecs:(NSTimeInterval)sec
{
    NSParameterAssert(block);
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * sec), globalQueue, block);
}

#pragma mark - /** 创建主线程 */
+ (void)executeInMainQueue:(dispatch_block_t)block
{
    NSParameterAssert(block);
    if ([NSThread isMainThread]) {
        block();
    } else {
        dispatch_async([ThreadDAO sharedInstance].mainQueue, block);
    }
}

+ (void)executeInSyncMainQueue:(dispatch_block_t)block
{
    NSParameterAssert(block);
    if ([NSThread isMainThread]) {
        block();
    } else {
        dispatch_sync([ThreadDAO sharedInstance].mainQueue, block);
    }
}

+ (void)executeInMainQueue:(dispatch_block_t)block afterDelaySecs:(NSTimeInterval)sec
{
    
    NSParameterAssert(block);
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * sec), [ThreadDAO sharedInstance].mainQueue, block);
}

#pragma mark - /** 创建线程 */
+ (void)executeInGlobalQueue:(dispatch_block_t)gBlock mainQueue:(dispatch_block_t)mBlock
{
    [self executeInGlobalQueue:^{
        gBlock();
        [self executeInMainQueue:mBlock];
    }];
    
}

+ (void)executeInCustomGlobalQueue:(dispatch_queue_t)globalQueue globalBlock:(dispatch_block_t)gBlock mainQueue:(dispatch_block_t)mBlock
{
    [self executeInCustomGlobalQueue:globalQueue block:^{
        gBlock();
        [self executeInMainQueue:mBlock];
    }];
}

#pragma mark - /** 创建有限制的线程 */
+ (void)executeInGlobalLimitQueue:(dispatch_block_t)gBlock mainQueue:(dispatch_block_t)mBlock
{
    [self executeInCustomGlobalQueue:GetLimitAsyncQueue() block:^{
        gBlock();
        [self executeInMainQueue:mBlock];
    }];
}

#pragma mark - /** 加入串行异步等待线程 */
+ (void)executeInWaitQueueWithKey:(NSString *)key handler:(handle_block_t)handler completion:(dispatch_block_t)completion
{
    if (!key.length) {
        return;
    }
    __block dispatch_group_t _group;
    __block dispatch_queue_t _quere;
    [self createQueueForTargetId:key complete:^(dispatch_group_t group, dispatch_queue_t queue) {
        _group = group;
        _quere = queue;
    }];
    
    /** 异步串行队列 */
    dispatch_async(_quere, ^{
        /** 累加次数 */
        [self addQueuesCount:key];
        /** 进入异步并行队列组 */
        dispatch_group_enter(_group);
        /** 同步或异步执行 */
        if (handler) {
            handler(^{
                /** 离开异步并行队列组 */
                dispatch_group_leave(_group);
            });
        } else {
            /** 离开异步并行队列组 */
            dispatch_group_leave(_group);
        }
        
        /** 等待并行队列组完成 */
        dispatch_group_wait(_group, DISPATCH_TIME_FOREVER);
        
        /** 队列组完成通知 */
        dispatch_group_notify(_group, dispatch_get_main_queue(), ^{
            /** 累减次数，当次数等于0，自动清除创建的队列 */
            [self removeQueuesCount:key];
            if (completion) {
                completion();
            }
        });
    });
}

#pragma mark - 挂起线程
+ (void)suspend:(dispatch_queue_t)queue
{
    dispatch_suspend(queue);
}

#pragma mark - 恢复线程
+ (void)resume:(dispatch_queue_t)queue
{
    dispatch_resume(queue);
}


#pragma mark - private

#pragma mark - 计算次数接口
+ (void)addQueuesCount:(NSString *)targetId
{
    ThreadDAO *thread = [self sharedInstance];
    NSNumber *num = [thread.targetIdCounts objectForKey:targetId];
    if (num == nil) {
        [thread.targetIdCounts setObject:@(1) forKey:targetId];
    } else {
        [thread.targetIdCounts setObject:@(num.integerValue + 1) forKey:targetId];
    }
}

+ (void)removeQueuesCount:(NSString *)targetId
{
    ThreadDAO *thread = [self sharedInstance];
    NSNumber *num = [thread.targetIdCounts objectForKey:targetId];
    
    NSInteger count = num.integerValue > 0 ? num.integerValue - 1 : 0;
    
    if (count == 0) {
        [thread.targetIdCounts removeObjectForKey:targetId];
        [thread.groupQueues removeObjectForKey:targetId];
        [thread.queues removeObjectForKey:targetId];
    } else {
        [thread.targetIdCounts setObject:@(count) forKey:targetId];
    }
    
}

+ (NSInteger)countOfQueues:(NSString *)targetId
{
    ThreadDAO *thread = [self sharedInstance];
    NSNumber *num = [thread.targetIdCounts objectForKey:targetId];
    return num.integerValue;
}

+ (void)createQueueForTargetId:(NSString *)targetId complete:(void (^)(dispatch_group_t group, dispatch_queue_t queue))complete
{
    if (complete == nil) return;
    
    ThreadDAO *thread = [self sharedInstance];
    dispatch_group_t group = [thread.groupQueues objectForKey:targetId];
    if (group == nil) {
        group = dispatch_group_create();
        [thread.groupQueues setObject:group forKey:targetId];
    }
    
    dispatch_queue_t queue = [thread.queues objectForKey:targetId];
    if (queue == nil) {
        const char* name = [[NSString stringWithFormat:@"com.ThreadDAO.WaitAsyncQueue.%@", targetId] UTF8String];
        queue = dispatch_queue_create(name, NULL);
        [thread.queues setObject:queue forKey:targetId];
    }
    
    complete(group, queue);
}
@end
